﻿// <copyright file="RateLimiting.cs" company="Microsoft Corporation">
// Copyright (c) 2009 Microsoft Corporation All Rights Reserved
// </copyright>
// <author>Michael S. Scherotter</author>
// <email>mischero@microsoft.com</email>
// <date>2009-05-07</date>
// <summary>RateLimiting class definition</summary>

namespace Synergist
{
    using System;
    using System.ComponentModel;
    using System.Linq;
    using System.Net;
    using System.Windows;
    using System.Windows.Threading;
    using NYTimes.Core;

    /// <summary>
    /// Web Client rate limiting
    /// </summary>
    public class RateLimiting
    {
        /// <summary>
        /// the dispatcher timer
        /// </summary>
        private DispatcherTimer timer;

        /// <summary>
        /// the queue of requests
        /// </summary>
        private System.Collections.Generic.Queue<Request> requests;

        /// <summary>
        /// Initializes a new instance of the RateLimiting class.
        /// </summary>
        /// <param name="apiName">the API Name</param>
        /// <param name="callsPerSecond">calls allowed per second</param>
        public RateLimiting(string apiName, int callsPerSecond)
        {
            this.requests = new System.Collections.Generic.Queue<Request>();

            if (SampleData.IsInDesignTool)
            {
                return;
            }

            string keyName = apiName + "QPS";

            if (Application.Current != null)
            {
                if (Application.Current.Resources.Contains(keyName))
                {
                    callsPerSecond = (int)Application.Current.Resources[keyName];
                }
            }

            int delay = 1000 / callsPerSecond;

            this.timer = new DispatcherTimer()
            {
                Interval = new TimeSpan(0, 0, 0, 0, delay)
            };

            this.timer.Tick += new EventHandler(this.OnTick);

            this.timer.Start();
        }

        /// <summary>
        /// Add a request to the queue
        /// </summary>
        /// <param name="client">the web client</param>
        /// <param name="address">the web address</param>
        public void MakeWebRequest(WebClient client, Uri address)
        {
            lock (this.requests)
            {
                var request = new Request()
                {
                    Address = address,
                    Client = client
                };

                this.requests.Enqueue(request);
            }
        }

        /// <summary>
        /// A web request has been cancelled so set its client to null.
        /// </summary>
        /// <param name="client">the web client</param>
        public void Cancel(WebClient client)
        {
            var toCancel = from request in this.requests
                          where request.Client == client
                          select request;

            foreach (var request in toCancel)
            {
                request.Client = null;
            }
        }

        /// <summary>
        /// dispatch timer tick hander
        /// </summary>
        /// <param name="sender">the dispatch timer</param>
        /// <param name="e">the event arguments</param>
        private void OnTick(object sender, EventArgs e)
        {
            lock (this.requests)
            {
                if (this.requests.Count > 0)
                {
                    var request = this.requests.Dequeue();

                    if (request.Client == null)
                    {
                        System.Diagnostics.Debug.WriteLine("Cancelled request: {0}", request.Address.ToString());
                    }
                    else
                    {
                        System.Diagnostics.Debug.WriteLine("Rate Limiting request: {0}", request.Address.ToString());

                        request.Client.DownloadStringAsync(request.Address);
                    }
                }
            }
        }

        /// <summary>
        /// A web request
        /// </summary>
        private class Request
        {
            /// <summary>
            /// Gets or sets the web client
            /// </summary>
            public WebClient Client { get; set; }

            /// <summary>
            /// Gets or sets the web address
            /// </summary>
            public Uri Address { get; set; }
        }
    }
}
